package gu.sql2java.manager.config.spring;

import java.lang.reflect.Method;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.gitee.l0km.casban.CommonPredicates;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;

import gu.sql2java.config.RuntimeConfig;

/**
 * HTTP请求拦截器，实现在HTTP请求解析之前为请求方法安装sql2java运行时配置对象，方法调用结束删除
 * @author guyadong
 * @since 3.32.6
 */
@Configuration
public class RuntimeConfigInterceptor implements AsyncHandlerInterceptor,WebMvcConfigurer{
	/**
	 * 应用层提供拦截对象的包名集合的对象ID,类型要求 {@code Set<String>} ,
	 * 如果应用层定义了拦截包名，则beanfilter拦截器则只拦截声明的类在指定包名下的类定义的方法
	 * 例如：
	 * <pre>
	 *     &#064;Bean
	 *     public Set&lt;String&gt; basePackagesForSql2javaInterceptor(){
	 *       return ImmutableSet.of("my.applicateion.package");
	 *     }
	 * </pre>
	 * 应用层提供此数据有助于拦截器更准确拦截方法，避免不必要的方法拦截
	 * 否则会拦截所有方法，参见  {@link FilterInterceptorConfig}
	 */
	private static final String INTERCEPT_PACKAGES = "basePackagesForSql2javaInterceptor";
	/** 根据应用层提供的拦截包名集合，创建的包名拦截器 */
	private final Predicate<String> packageFilter;
	public RuntimeConfigInterceptor(
			@Qualifier(INTERCEPT_PACKAGES) @Autowired( required =  false) Set<String>interceptPackages) {
		/** 包过滤器，如果 interceptPackages 为空则允许任意包名 */
		this.packageFilter = interceptPackages == null ? null  
				:  CommonPredicates.basePackageFilter(ImmutableSet.copyOf(interceptPackages));
	}
	
	/**
	 * 判断目标是否需要拦截
	 * @param handler
	 */
	private boolean needIntercept(Object handler) {
		if(handler instanceof HandlerMethod) {
			Method method = ((HandlerMethod)handler).getMethod();
			return null == packageFilter ||  packageFilter.apply(method.getDeclaringClass().getPackage().getName());
		}
		return false;
	}
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		if(needIntercept(handler)) {
			Method method = ((HandlerMethod)handler).getMethod();
			RuntimeConfig.installConfigOf(method);
		}
		return true;
	}
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		RuntimeConfig.removeLocalConfig();
	}
	@Override
	public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		/**
		 * 当WEB请求为异步请求时，不会调用afterCompletion，需要实现此方法才能清理thread-local 变量
		 * 参见 org.springframework.web.servlet.DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response)方法实现代码
		 * */
		afterCompletion(request, response, handler,null);
	}
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(this)
				.addPathPatterns("/**")  /** 添加拦截路径 */
				.excludePathPatterns("/error","/swagger-resources/**","/swagger/**", "/swagger2/**");/** 排除的拦截路径（此路径不拦截 */
	}

}
